package cc.shacocloud.mirage.bean.impl;

import cc.shacocloud.mirage.bean.BeanKeyLoader;
import cc.shacocloud.mirage.bean.ClassScanner;
import cc.shacocloud.mirage.bean.QualifierHandler;
import cc.shacocloud.mirage.bean.bind.Component;
import cc.shacocloud.mirage.bean.bind.ComponentScan;
import cc.shacocloud.mirage.bean.meta.BeanKey;
import cc.shacocloud.mirage.utils.ClassUtil;
import cc.shacocloud.mirage.utils.annotation.AnnotatedElementMetadata;
import cc.shacocloud.mirage.utils.annotation.AnnotatedElementUtils;
import cc.shacocloud.mirage.utils.collection.ArrayUtil;
import cc.shacocloud.mirage.utils.collection.CollUtil;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

/**
 * 基于 {@link Component} 实现 {@link BeanKeyLoader} 的对象描述加载器，遇到标识 {@link ComponentScan} 将会进行嵌套扫描
 *
 * @author 思追(shaco)
 */
@Slf4j
public class AnnotationBeanKeyLoader implements BeanKeyLoader {
    
    private static final Class<Component> COMPONENT_ANNOTATION = Component.class;
    private static final Class<ComponentScan> COMPONENT_SCAN_ANNOTATION = ComponentScan.class;
    
    @NotNull
    private final ClassScanner classScanner;
    
    @NotNull
    private final QualifierHandler qualifierHandler;
    
    public AnnotationBeanKeyLoader(@NotNull ClassScanner classScanner,
                                   @NotNull QualifierHandler qualifierHandler) {
        this.classScanner = classScanner;
        this.qualifierHandler = qualifierHandler;
    }
    
    @Override
    public Set<BeanKey> findBeanKeys(@NotNull String @NotNull ... basePackages) {
        return doFindBeanKeys(new HashSet<>(), basePackages);
    }
    
    @Override
    public Set<BeanKey> findBeanKeys(@NotNull Class<?> @NotNull ... baseClasses) {
        return doFindBeanKeys(new HashSet<>(), baseClasses);
    }
    
    /**
     * 使用 Set 对处理过的 Class 进行过滤
     */
    protected Set<BeanKey> doFindBeanKeys(@NotNull Set<Class<?>> classSet, @NotNull String @NotNull ... basePackages) {
        Set<Class<?>> classes = classScanner.findClasses(basePackages);
        return doFindBeanKeys(classSet, classes.toArray(Class[]::new));
    }
    
    protected Set<BeanKey> doFindBeanKeys(@NotNull Set<Class<?>> classSet, @NotNull Class<?> @NotNull ... baseClasses) {
        long start = log.isDebugEnabled() ? System.currentTimeMillis() : 0;
        
        Set<BeanKey> beanKeySet = new HashSet<>(baseClasses.length, 1);
        
        Set<String> scanBasePackages = new HashSet<>();
        Set<Class<?>> scanBasePackageClasses = new HashSet<>();
        
        for (Class<?> clazz : baseClasses) {
            
            // 避免重复消费
            if (classSet.contains(clazz)) {
                continue;
            }
            classSet.add(clazz);
            
            
            AnnotatedElementMetadata annotatedMetadata = AnnotatedElementUtils.getAnnotatedMetadata(clazz);
            if (annotatedMetadata.isAnnotationPresent(COMPONENT_ANNOTATION)) {
                String qualifier = qualifierHandler.getQualifier(clazz);
                beanKeySet.add(new BeanKey(clazz, qualifier));
            }
            
            // 如果定义了扫描注解
            if (annotatedMetadata.isAnnotationPresent(COMPONENT_SCAN_ANNOTATION)) {
                ComponentScan annotation = Objects.requireNonNull(annotatedMetadata.getAnnotation(COMPONENT_SCAN_ANNOTATION));
                
                String[] basePackages = annotation.scanBasePackages();
                Class<?>[] basePackageClasses = annotation.scanBasePackageClasses();
                
                // 如果基础扫描路径并且扫描类型为空，则使用当前类的包名作为扫描路径
                if (ArrayUtil.isEmpty(basePackages) && ArrayUtil.isEmpty(basePackageClasses)) {
                    basePackages = new String[]{ClassUtil.getPackageName(clazz)};
                }
                
                scanBasePackages.addAll(Arrays.asList(basePackages));
                scanBasePackageClasses.addAll(Arrays.asList(basePackageClasses));
            }
            
        }
        
        if (log.isDebugEnabled()) {
            long stop = System.currentTimeMillis();
            log.debug("在 {} 个类中扫描组件类耗时 {} ms，扫描到 {} 个组件类", baseClasses.length, stop - start, beanKeySet.size());
        }
        
        if (CollUtil.isNotEmpty(scanBasePackages)) {
            Set<BeanKey> beanKeys = doFindBeanKeys(classSet, scanBasePackages.toArray(String[]::new));
            beanKeySet.addAll(beanKeys);
        }
        
        if (CollUtil.isNotEmpty(scanBasePackageClasses)) {
            
            // 删除刚刚已经扫描过的类型
            for (Class<?> baseClass : baseClasses) {
                scanBasePackageClasses.remove(baseClass);
            }
            
            
            Set<BeanKey> beanKeys = doFindBeanKeys(classSet, scanBasePackageClasses.toArray(Class[]::new));
            beanKeySet.addAll(beanKeys);
        }
        
        return beanKeySet;
    }
    
}
